package com.elite.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;

/**
 * spring boot 启动会加载这个三个自动配置类：
 * 1. org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
 * 2. org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
 * 		这个是配置我们的用户登录服务查询
 * 3. org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
 * 		SpringSecurity本质原理就是过滤器链，这个过滤器链是在 DelegatingFilterProxy 这个过滤器创建的
 * 		最后把 DelegatingFilterProxy 这个过滤器添加 servletContext中
 * 		在请求进来经过这个 DelegatingFilterProxy 过滤器，这个过滤器会通过 springSecurityFilterChain 这个名称去 ioc 获取 过滤器链 最后调用！！！
 *
 *
 */
@EnableWebSecurity
@Configuration
public class SecurityFilterConfig {


	/**
	 * 测试授权源码，并描述涉及到的对象关系
	 * @param http
	 * @return
	 * @throws Exception
	 */
	@Bean
	public SecurityFilterChain securityFilterChain3(HttpSecurity http) throws Exception {



		/**
		 * 开启http请求的权限保护。
		 * 	1. 创建一个授权过滤器
		 *  2. 创建一个 AuthorizationManagerRequestMatcherRegistry 授权管理请求匹配注册器 并返回（核心对象）
		 */
		AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry = http.authorizeHttpRequests();

		/**
		 * 1.添加要授权的请求路径，这个请求路径会封装为 RequestMatcher 对象类型（实现类是：MvcRequestMatcher），最后会存放到 AuthorizationManagerRequestMatcherRegistry（授权管理请求匹配注册器）  unmappedMatchers 集合属性中。
		 * 		private List<RequestMatcher> unmappedMatchers;
		 * 2.最后创建 AuthorizedUrl 对象返回，该对象里面的 matchers 属性 也是和 AuthorizationManagerRequestMatcherRegistry（授权管理请求匹配注册器）的 unmappedMatchers属性一样是存储 RequestMatcher对象
		 * 		private final List<? extends RequestMatcher> matchers;
		 * 3. AuthorizationManagerRequestMatcherRegistry 的 unmappedMatchers 属性和 AuthorizedUrl 的 matchers 属性，它们的 RequestMatcher 是同一个对象地址的。
		 *
		 *
		 */
		AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = authorizationManagerRequestMatcherRegistry.requestMatchers("/hello/**");
		// 为 /hello/** 分配授权管理器
		authorizedUrl.permitAll();


		AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl1 = authorizationManagerRequestMatcherRegistry.requestMatchers("/test/**");

		// 为 /test/** 路径分配只有 system 权限权限管理器
//		authorizedUrl1.hasAuthority("system");
		// 为 /test/** 路径分配角色管理器
		authorizedUrl1.hasRole("system");

		/**
		 * 最后 authenticated(); 这一步是 为这个 authorizedUrl 授权对象，构建一个 认证授权管理器 ，
		 * 而这个 AuthenticatedAuthorizationManager 授权管理对象，该对象是真正干活的。鉴定用户是否已认证 或者 认证了但是有没有权限访问的操作判断逻辑就是在这对象里面
		 *
		 * 从以上代码分析  authorizedUrl 里面 matchers 集合属性，是存储到你要认证授权的路径，它是怎么认证认证授权的？就是 在 AuthenticatedAuthorizationManager 这个对象认证授权的
		 *	AuthenticatedAuthorizationManager 	  --> matchers（你要认证授权的n个路径），他们关系是  1 对 多的关系，最后会根据你的认证授权路径匹配 最终调用 AuthenticatedAuthorizationManager 类方法 匹配
		 *
		 */
		// 除上面外的所有请求全部需要鉴权认证
		// 所有请求 都要认证
//		authorizationManagerRequestMatcherRegistry.anyRequest().authenticated();
		// 该请求路径需要认证
		authorizationManagerRequestMatcherRegistry.requestMatchers("/demo/security","/index","/csrf").authenticated();
		// 为该请求路径 分配一个不需要认证授权的管理器
		authorizationManagerRequestMatcherRegistry.requestMatchers("/demo","/cors").permitAll();
		//认证的方式默认有：表单方式、以及httpBasic 成功
		http.formLogin();
		// 记住我,设置记住我签名密钥 1234
		http.rememberMe().key("1234");
//		http.httpBasic();
		//开启会话管理
		http.sessionManagement(
				(sessionManagement)->{
					// 设置 session 最大为1个
					sessionManagement.maximumSessions(1);
					// 添加 csrf 认证策略 （结合开启 csrf 防御 使用）
//					sessionManagement.sessionAuthenticationStrategy(new CsrfAuthenticationStrategy())

				}
		);

		// 开启csrf 防御
		http.csrf(m->{
			// 设置那些请求路径不需要 csrf 防御
//			m.ignoringRequestMatchers("/test");
			// 设置 csrf 令牌存储方式，是 cookie ？还是 session ？ 还是自定义？
			m.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
		});

		// 配置跨域
		http.cors(m->{

			/**
			 * origins：允许那些地址可以访问
			 * allowedHeaders：允许那些请求头，* 代表所有
			 * exposedHeaders：可以暴露那些响应头，* 代表所有
			 * methods：允许哪些请求方式，* 代表所有
			 * maxAge：预检 请求有效期，有效期内不用再发生预检请求，默认1800秒
			 * allowCredentials：游览器是否应当发送凭证信息，如;cookie 信息
			 */
		});

		//构建过滤链
		DefaultSecurityFilterChain filterChain = http.build();
		return filterChain;
	}


//	/**
//	 * 测试自定义过滤器代理对象
//	 * @param http
//	 * @return
//	 * @throws Exception
//	 */
//	@Bean
//	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//
//		/**
//		 * 开启http请求的权限保护。
//		 * 	1. 创建一个授权过滤器
//		 *  2. 创建一个 AuthorizationManagerRequestMatcherRegistry 授权管理请求匹配注册器 并返回（核心对象）
//		 */
//		AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry = http.authorizeHttpRequests();
//
//		authorizationManagerRequestMatcherRegistry.requestMatchers("/login","/demo","openDemo","/closeDemo").permitAll();
//
//		//构建过滤链
//		DefaultSecurityFilterChain filterChain = http.build();
//		return filterChain;
//	}



	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() throws Exception {

		return (webSecurity)->{
			webSecurity.ignoring().requestMatchers("/hello");
		};
	}
}
